#pragma once

#include "requestor.hpp"

namespace myrpc
{
    namespace client
    {
        class Provider
        {
            public:
                using ptr=std::shared_ptr<Provider>;
                Provider(const Requestor::ptr &requestor)
                    :_requestor(requestor)
                {}

                bool registerMethod(const BaseConnection::ptr &conn,const std::string &method,const Address &host)
                {
                    auto req_msg=MessageFactory::create<ServiceRequest>();
                    req_msg->setId(UUID::uuid());
                    req_msg->setHost(host);
                    req_msg->setMethod(method);
                    req_msg->setMType(MType::REQ_SERVICE);
                    req_msg->setServiceOptype(ServiceOptype::SERVICE_REGISTRY);
                    
                    BaseMessage::ptr rsp_msg;
                    bool ret=_requestor->send(conn,req_msg,rsp_msg);
                    if(ret==false)
                    {
                        ELOG("%s 服务注册失败！",method.c_str());
                        return false;
                    }
                    auto service_rsp=std::dynamic_pointer_cast<ServiceResponse>(rsp_msg);
                    if(service_rsp.get()==nullptr)
                    {
                        ELOG("响应类型向下转化失败！");
                        return false;
                    }
                    if(service_rsp->rcode()!=RCode::RCODE_OK)
                    {
                        ELOG("服务注册失败，原因是 %s",errReason(service_rsp->rcode()).c_str());
                    }
                    return true;
                }
            private:
                Requestor::ptr _requestor;
        };

        class MethodHost
        {
            public:
                using ptr=std::shared_ptr<MethodHost>;
                MethodHost()
                    :_idx(0)
                {}
                MethodHost(const std::vector<Address> &hosts)
                    :_hosts(hosts),
                    _idx(0)
                {}
                void appendHost(const Address &host)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _hosts.push_back(host);
                }
                void removeHost(const Address &host)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    for(auto it=_hosts.begin();it!=_hosts.end();++it)
                    {
                        if(*it==host)
                        {
                            _hosts.erase(it);
                            break;
                        }
                    }
                }
                Address chooseHost()
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    size_t pos=_idx++%_hosts.size();
                    return _hosts[pos];
                }
                bool empty()
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    return _hosts.empty();
                }
            private:
                std::mutex _mutex;
                size_t _idx;
                std::vector<Address> _hosts;
        };
        class Discoverer
        {
            public:
                using OfflineCallback=std::function<void(const Address&)>;
                using ptr=std::shared_ptr<Discoverer>;
                Discoverer(const Requestor::ptr &requestor,const OfflineCallback& cb)
                    :_requestor(requestor),
                    _offline_callback(cb)
                {}
                //服务发现
                bool serviceDiscovery(const BaseConnection::ptr &conn,  const std::string &method,Address &host)
                {
                    {
                        //当前所保管的提供者信息存在，则直接返回地址
                        std::unique_lock<std::mutex> lock(_mutex);
                        auto it=_method_hosts.find(method);
                        if(it!=_method_hosts.end())
                        {
                            host=it->second->chooseHost();
                            return true;
                        }
                    }

                    //当前服务的提供者为空，进行服务发现请求
                    auto msg_req=MessageFactory::create<ServiceRequest>();
                    msg_req->setId(UUID::uuid());
                    msg_req->setMType(MType::REQ_SERVICE);
                    msg_req->setServiceOptype(ServiceOptype::SERVICE_DISCOVER);
                    msg_req->setMethod(method);
                    BaseMessage::ptr msg_rsp;
                    bool ret=_requestor->send(conn,msg_req,msg_rsp);
                    if(ret==false)
                    {
                        ELOG("服务发现失败！");
                        return false;
                    }
                    auto service_rsp=std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                    if(!service_rsp)
                    {
                        ELOG("服务发现失败，响应类型向下转换失败！");
                        return false;
                    }
                    if(service_rsp->rcode()!=RCode::RCODE_OK)
                    {
                        ELOG("服务发现失败,%s",errReason(service_rsp->rcode()).c_str());
                        return false;
                    }

                    //能走到这里，当前对应的服务是没有提供主机的
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto method_host=std::make_shared<MethodHost>(service_rsp->hosts());
                    _method_hosts[method]=method_host;

                    if(method_host->empty())
                    {
                        ELOG("服务发现失败，没有能够提供服务的主机！");
                        return false;
                    }

                    host=method_host->chooseHost();
                    return true;
                }
                //这个模块是提供给Dispatcher模块进行服务上线下线请求处理通知的回调函数
                void onServiceRequest(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
                {
                    //判断是上线还是下线请求，如果都不是就不用处理了

                    //上线请求：找到对应的MethodHost，向其中新增一个主机地址
                    if(msg->serviceOptype()==ServiceOptype::SERVICE_ONLINE)
                    {
                        std::unique_lock<std::mutex> lock(_mutex);
                        auto it=_method_hosts.find(msg->method());
                        if(it==_method_hosts.end())
                        {
                            auto method_host=std::make_shared<MethodHost>();
                            method_host->appendHost(msg->host());

                            _method_hosts[msg->method()]=method_host;
                        }
                        else{
                            it->second->appendHost(msg->host());
                        }

                    }
                    //下线请求：找到对应的MethodHost，向其中删除一个主机地址
                    else if(msg->serviceOptype()==ServiceOptype::SERVICE_OFFLINE)
                    {
                        std::unique_lock<std::mutex> lock(_mutex);
                        auto it=_method_hosts.find(msg->method());
                        if(it==_method_hosts.end())
                        {
                            return ;
                        }
                        it->second->removeHost(msg->host());
                        _offline_callback(msg->host());
                    }

                }
            private:
                OfflineCallback _offline_callback;
                std::mutex _mutex;
                std::unordered_map<std::string,MethodHost::ptr> _method_hosts;
                Requestor::ptr _requestor;

        };
    }
}